/* * Copyright 2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.statemachine.config.common.annotation; import java.lang.annotation.Annotation; import java.util.List; import org.springframework.aop.scope.ScopedProxyUtils; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinitionHolder; import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanNameGenerator; import org.springframework.beans.factory.support.DefaultBeanNameGenerator; import org.springframework.context.EnvironmentAware; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.ScopedProxyMode; import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.env.Environment; import org.springframework.core.type.AnnotationMetadata; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; /** * Base class for {@link Configuration} which works on a bean definition level * relying on {@link ImportBeanDefinitionRegistrar} phase to register beans. * * @author Janne Valkealahti * * @param <O> The object that used builder returns * @param <B> The type of the builder */ public abstract class AbstractImportingAnnotationConfiguration<B extends AnnotationBuilder<O>, O> implements ImportBeanDefinitionRegistrar, BeanFactoryAware, EnvironmentAware { private BeanFactory beanFactory; private Environment environment; private final BeanNameGenerator beanNameGenerator = new DefaultBeanNameGenerator(); @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { List<Class<? extends Annotation>> annotationTypes = getAnnotations(); Class<? extends Annotation> namedAnnotation = null; String[] names = null; ScopedProxyMode proxyMode = null; if (annotationTypes != null) { for (Class<? extends Annotation> annotationType : annotationTypes) { AnnotationAttributes attributes = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes( annotationType.getName(), false)); if (attributes != null && attributes.containsKey("name")) { names = attributes.getStringArray("name"); namedAnnotation = annotationType; break; } } } // check if Scope annotation is defined and get proxyMode from it AnnotationAttributes scopeAttributes = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes( Scope.class.getName(), false)); if (scopeAttributes != null) { proxyMode = scopeAttributes.getEnum("proxyMode"); } BeanDefinition beanDefinition; try { beanDefinition = buildBeanDefinition(importingClassMetadata, namedAnnotation); } catch (Exception e) { throw new RuntimeException("Error with onConfigurers", e); } // implementation didn't return definition so don't continue registration if (beanDefinition == null) { return; } if (ObjectUtils.isEmpty(names)) { // ok, name(s) not given, generate one names = new String[] { beanNameGenerator.generateBeanName(beanDefinition, registry) }; } registry.registerBeanDefinition(names[0], beanDefinition); if (names.length > 1) { for (int i = 1; i < names.length; i++) { registry.registerAlias(names[0], names[i]); } } // wrap in scoped proxy if needed if (proxyMode != null && proxyMode != ScopedProxyMode.DEFAULT && proxyMode != ScopedProxyMode.NO) { BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(beanDefinition, names[0]); BeanDefinitionHolder scopedProxy = null; if (proxyMode == ScopedProxyMode.TARGET_CLASS) { scopedProxy = ScopedProxyUtils.createScopedProxy(definitionHolder, registry, true); } else if (proxyMode == ScopedProxyMode.INTERFACES) { scopedProxy = ScopedProxyUtils.createScopedProxy(definitionHolder, registry, false); } else { throw new IllegalArgumentException("Unknown proxyMode " + proxyMode); } BeanDefinitionReaderUtils.registerBeanDefinition(scopedProxy, registry); } } @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { Assert.isInstanceOf(ListableBeanFactory.class, beanFactory, "beanFactory be of type ListableBeanFactory but was " + beanFactory); this.beanFactory = beanFactory; } @Override public void setEnvironment(Environment environment) { this.environment = environment; } /** * Called to get a bean definition to register. * * @param importingClassMetadata annotation metadata of the importing class * @param namedAnnotation found annotations for bean names * @return the bean definition to register * @throws Exception if error occurred */ protected abstract BeanDefinition buildBeanDefinition(AnnotationMetadata importingClassMetadata, Class<? extends Annotation> namedAnnotation) throws Exception; /** * Gets the annotations specific for this configurer. * * @return the annotations */ protected abstract List<Class<? extends Annotation>> getAnnotations(); /** * Gets the bean factory. * * @return the bean factory */ protected BeanFactory getBeanFactory() { return beanFactory; } /** * Gets the environment. * * @return the environment */ protected Environment getEnvironment() { return environment; } protected abstract static class BeanDelegatingFactoryBean<T, B extends AnnotationBuilder<O>, O> implements FactoryBean<T>, BeanFactoryAware, InitializingBean, DisposableBean { private final B builder; private T object; private List<AnnotationConfigurer<O, B>> configurers; private BeanFactory beanFactory; private Class<T> clazz; public BeanDelegatingFactoryBean(B builder, Class<T> clazz) { this.builder = builder; this.clazz = clazz; } @Override public Class<?> getObjectType() { return clazz; } @Override public T getObject() throws Exception { return object; } @Override public boolean isSingleton() { return true; } @Autowired(required = false) public void setConfigurers(List<AnnotationConfigurer<O, B>> configurers) { this.configurers = configurers; } @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { this.beanFactory = beanFactory; } @Override public void destroy() throws Exception { } public B getBuilder() { return builder; } public List<AnnotationConfigurer<O, B>> getConfigurers() { return configurers; } protected void setObject(T object) { this.object = object; } protected BeanFactory getBeanFactory() { return beanFactory; } } }